<script setup lang="ts">
import { nextTick, onMounted, ref, type Ref } from 'vue'
import CryptoJS from 'crypto-js'
import { ElMessage, ElMessageBox, ElScrollbar } from 'element-plus'
import { useRecord, useVoice } from '@/composables'
// import VConsole from 'vconsole'
import { languages, currentLanguage, type LanguageType } from '@/shared'
import { Microphone, UserFilled } from '@element-plus/icons-vue'
import { simpleToTradition } from 'chinese-simple2traditional'
import Cookies from 'js-cookie'

// 语音识别相关
const {
  initRecorder,
  recordDialog,
  recordMessage,
  recordText,
  closeRecordDialog,
  recordingDialogVisible,
  endRecord,
  startRecord
} = useRecord()

// 语音合成相关
const { connectVoiceWebSocketUrl, initAudioPlayer, audioPlayer } = useVoice()

// 所有聊天记录
const conversations: Ref<{ role: 'user' | 'assistant'; content: string; id: number }[]> = ref([])

// 按下开始语音识别
function pressRecord() {
  audioPlayer.value.stop()
  startRecord()
}

// 确认发送语音识别结果
function confirmRecord() {
  if (recordMessage.value) {
    conversations.value.push({
      id: Date.now(),
      role: 'user',
      content: recordMessage.value
    })
    getAnswer()
    closeRecordDialog()
  } else {
    ElMessage({
      type: 'error',
      message: '网络异常，请重说'
    })
  }
}

// 生成回答的 websocket 实例
let answerWS: WebSocket

// 用于取消等待回答过程中的 ...
let st: number

// 生成回答数据
function getAnswer() {
  const websocketUrl = getAnswerWebsocketUrl()
  answerWS = new WebSocket(websocketUrl)
  answerWS.onopen = () => {
    tempAnswer = ''
    sendQuestion()
    conversations.value.push({
      role: 'assistant',
      content: '思考中',
      id: Date.now()
    })
    st = setInterval(() => {
      conversations.value.slice(-1)[0].content += '.'
    }, 1000)
    scrollToBottom()
  }
  answerWS.onmessage = (e) => {
    renderAnswerResult(e.data)
  }
  answerWS.onclose = () => {
    const filteredText = textFilter(tempAnswer)
    conversations.value.slice(-1)[0].content =
      currentLanguage.value === 'cantonese' ? simpleToTradition(filteredText) : filteredText
    connectVoiceWebSocketUrl(conversations.value.slice(-1)[0].content)
    clearInterval(st)
    scrollToBottom()
  }
}

let tempAnswer = ''

// 要过滤替换的词汇
const filterWords = [
  {
    originWord: ['科大讯飞'],
    replaceWord: '中山智能移动机器人研究院'
  },
  {
    originWord: [
      '认知智能模型',
      '认知智能大模型',
      '讯飞星火认知大模型',
      '星火认知大模型',
      '人工智能語言模型',
      'AI語言模型',
      '語言模型',
      'AI助手'
    ],
    replaceWord: 'IM小狗'
  }
]

// 过滤替换掉敏感的词汇
function textFilter(answer: string) {
  let result = answer
  filterWords.forEach((item: { originWord: string[]; replaceWord: string }) => {
    item.originWord.forEach((word: string) => {
      result = result.replaceAll(word, item.replaceWord)
    })
  })
  return result
}

// 解析出回答内容
function renderAnswerResult(resultData: string) {
  let jsonData = JSON.parse(resultData)
  tempAnswer += jsonData.payload.choices.text[0].content
}

// 发送问题
function sendQuestion() {
  const params = {
    header: {
      app_id: (window as any).APP_ID
    },
    parameter: {
      chat: {
        domain: 'generalv3'
      }
    },
    payload: {
      message: {
        text: [...conversations.value]
      }
    }
  }
  answerWS.send(JSON.stringify(params))
}

// 生成连接生成回答的 websocket
function getAnswerWebsocketUrl() {
  let url = 'wss://spark-api.xf-yun.com/v3.1/chat'
  const host = location.host
  const apiKey = (window as any).API_KEY
  const apiSecret = (window as any).API_SECRET
  const date = new Date().toUTCString()
  const algorithm = 'hmac-sha256'
  const headers = 'host date request-line'
  const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v3.1/chat HTTP/1.1`
  const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
  const signature = CryptoJS.enc.Base64.stringify(signatureSha)
  const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
  const authorization = btoa(authorizationOrigin)
  url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
  return url
}

// 清空聊天记录
function restartConversation() {
  conversations.value.length = 0
}

// 滚动条组件实例
const scrollbarRef: Ref<InstanceType<typeof ElScrollbar> | undefined> = ref()
// 滚动条内容器元素
const innerRef: Ref<HTMLDivElement | undefined> = ref()
// 滚动到底部
function scrollToBottom() {
  nextTick(() => {
    scrollbarRef.value!.setScrollTop(innerRef.value!.offsetHeight || 0)
  })
}

// 监听到切换语言
function handleLanguageChange(val: string) {
  Cookies.set('lang', val)
}

onMounted(() => {
  currentLanguage.value = (Cookies.get('lang') as LanguageType) || 'cantonese'
  // new VConsole({ theme: 'dark' })
  initRecorder()
  initAudioPlayer()
  ElMessageBox.alert(
    languages[currentLanguage.value].welcomeText[0],
    languages[currentLanguage.value].welcomeText[1],
    {
      confirmButtonText: '好的',
      center: true
    }
  )
})
</script>

<template>
  <el-container class="h-full select-none">
    <el-header class="!p-0" height="92px">
      <div class="h-full flex flex-col">
        <el-button @click="restartConversation" class="flex-1 w-full !rounded-none" type="primary"
          >清空聊天</el-button
        >
        <el-select v-model="currentLanguage" class="w-full" @change="handleLanguageChange">
          <el-option
            v-for="key in Object.keys(languages)"
            :key="key"
            :value="key"
            :label="languages[key].label"
          ></el-option>
        </el-select>
      </div>
    </el-header>
    <el-main class="bg-[#eeeeee]">
      <el-scrollbar class="h-full" ref="scrollbarRef">
        <div ref="innerRef">
          <template v-for="conversation in conversations" :key="conversation.id">
            <div v-if="conversation.role === 'user'" class="py-1 flex justify-end pl-10">
              <div class="flex-1 flex justify-end">
                <span class="bg-[#409eff] text-white rounded p-2 break-all">{{
                  conversation.content
                }}</span>
              </div>
              <div class="p-2 w-10 text-center">
                <el-icon :size="20" color="#4D99F9">
                  <UserFilled />
                </el-icon>
              </div>
            </div>
            <div v-if="conversation.role === 'assistant'" class="py-1 flex pr-10">
              <div class="p-2 w-10 text-center">🤖</div>
              <div class="flex-1 flex">
                <span class="bg-white rounded p-2 break-all">
                  {{ conversation.content }}
                </span>
              </div>
            </div>
          </template>
        </div>
      </el-scrollbar>
    </el-main>
    <el-footer class="!p-0">
      <el-button
        @touchstart.prevent="pressRecord"
        @touchend.prevent="endRecord"
        class="!h-full w-full !rounded-none"
        type="primary"
        >{{ languages[currentLanguage].pressText }}</el-button
      >
    </el-footer>
  </el-container>
  <el-dialog
    v-model="recordDialog"
    title="识别结果"
    width="80%"
    align-center
    :before-close="closeRecordDialog"
  >
    <span>{{ recordMessage }}</span>
    <template #footer>
      <el-button type="info" @click="closeRecordDialog">重说</el-button>
      <el-button type="primary" @click="confirmRecord">发送</el-button>
    </template>
  </el-dialog>
  <el-dialog
    v-model="recordingDialogVisible"
    width="80%"
    :title="recordText"
    align-center
    :before-close="closeRecordDialog"
  >
    <div class="text-center">
      <el-icon :size="100">
        <Microphone />
      </el-icon>
    </div>
  </el-dialog>
</template>

<style scoped></style>
